package de.stefanocke.roo.wicket.model

import de.stefanocke.roo.wicket.Stereotypes
import de.stefanocke.roo.wicket.ValidationException
import de.stefanocke.roo.wicket.base.WicketItdBuilder
import de.stefanocke.roo.xtend.Method
import java.lang.reflect.Modifier
import java.util.ArrayList
import org.springframework.roo.classpath.details.FieldMetadataBuilder
import org.springframework.roo.model.JavaType

import static de.stefanocke.roo.xtend.Method.*
import static de.stefanocke.roo.xtend.MethodSignature.*
import static de.stefanocke.roo.xtend.ModifierHelper.*
import static de.stefanocke.roo.xtend.Parameter.*

import static extension de.stefanocke.roo.wicket.base.WicketItdBuilder.*
import static extension de.stefanocke.roo.xtend.RooExtensions.*

class BeanPropertiesMetadataItdBuilder extends WicketItdBuilder {
	
	override BeanPropertiesMetadata getMetadata(){super.metadata as BeanPropertiesMetadata} 
	def properties(){metadata.getProperties}
	
	override getInterfaces(){
			newArrayList("org.apache.wicket.model.IDetachable".javaType)
	}
	
	override getMethods()
	{
		val methods = new ArrayList<Method>()
		methods += constructors
		methods += getAnnotationsMethod
		methods += detachMethod
		methods			
	}
	
	//public static AnnotationMap CREATED_ANNOTATIONS = getAnnotations(Post.class, "CREATED", "created");
	override getFields()
	{	
		val fields = new ArrayList<FieldMetadataBuilder>()
		fields+=properties.map[annotationsField]
		fields+=contextFields
		fields		
	}
	
	def annotationsField(BeanPropertyMetadata p){
		val utilMethod = utilMethod();
		field(or(Modifier::PUBLIC, Modifier::STATIC), utilMethod.returnType, 
			p.getFieldAnnotationsConstantName.symbol, 
			'''«utilMethod.methodName»(«modelClass.resolveStatic».class, "«p.getFieldConstantName»", "«p.getField.fieldName»")'''.toString
		)
		//TODO: Geerbte Felder in der Entity sind NICHT in der modelClass deklariert!...
		
	}
	
	//	private static AnnotationMap getAnnotations(Class<Post> modelClass,	String fieldName, String modelClassFieldName) {
	//		return AnnotationUtils.getAnnotations(modelClass, MyPostPanel.class, fieldName, modelClassFieldName);
	//	}
	
	def getAnnotationsMethod(){
		val utilDetails = Stereotypes::ANNOTATION_UTILS.stereotypeDetails
		
			
			
		method(signature(or(Modifier::PRIVATE, Modifier::STATIC), 
			utilMethod.returnType, utilMethod.methodName.symbolName, 
			param(typeof(Class).javaType(WILDCARD), "modelClass"),
			param(typeof(String).javaType, "fieldName"),
			param(typeof(String).javaType, "modelClassFieldName")
		), [|'''
			return «utilDetails.name.resolveStatic».«utilMethod.methodName.symbolName»(modelClass, «metadata.destination.resolveStatic».class, fieldName, modelClassFieldName);
		'''])
	}
	
	def utilMethod(){
		val utilDetails = Stereotypes::ANNOTATION_UTILS.stereotypeDetails
		val utilMethod = utilDetails?.declaredMethods
			.filter [
				it.stereotypeAnnotation?.getAttribute("value")?.value
				?.equals(Stereotypes::ANNOTATION_UTILS_GET_ANNOTATIONS)
			]
			.head
		if(utilMethod==null){
			throw new ValidationException("Method with Stereotype "+Stereotypes::ANNOTATION_UTILS_GET_ANNOTATIONS+" not found.");
		}
		utilMethod
	}
	
	def contextProperties(){
		properties.filter[isContext]
	}
	
	def Iterable<FieldMetadataBuilder> getContextFields(){	
		//One field for each init param
		contextProperties.map(p | modelField(p.type, p.propertyName))		
	}
		

	def getConstructors(){
		newArrayList(
			constructorWithAllContextFields
		)
	}
	
	def constructorWithAllContextFields(){
		val params = contextProperties.map(p | modelParam(p.type, p.propertyName)).toList
		//TODO: The call to no-arg constructor is a quick workaround for calling the constructor that does the injection for Roo persistence
		//We need some mechanism where multiple ITDs can "contribute" to the constructor
		if(!contextProperties.empty){ 
			method(
				signature(params),	
				[|'''
				this();
				
				«FOR p : contextProperties»
					this.«p.propertyName»=«p.propertyName»;
				«ENDFOR»
				''']
			)
		}
	}
	
	
		//method for detaching the initParams
	def getDetachMethod(){
		
			method(
				signature(Modifier::PUBLIC, 
					JavaType::VOID_PRIMITIVE, 
					"detach"),
				[|'''
				«FOR p : contextProperties»
					if(«p.propertyName»!=null){
						«p.propertyName».detach();
					}
				«ENDFOR»
				return;
				''']
			)	
		
	}
}
